use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::{Acquire, Release};
use std::sync::{Arc, RwLock};
use std::thread::{JoinHandle, sleep, spawn};
use std::time::Duration;

pub struct AsyncCalScreen {
    pub cal_screen_string: Arc<RwLock<Option<String>>>,
    pub cache_out: Arc<RwLock<Vec<String>>>,
    pub cache_ready_flag: AtomicBool,
}
impl AsyncCalScreen {
    pub fn async_set_cal_screen_string(
        self: Arc<Self>,
        cal_screen_string: String,
    ) -> JoinHandle<()> {
        spawn(move || {
            loop {
                let mut locker = self.cal_screen_string.write().unwrap();
                match locker.as_ref() {
                    None => {
                        locker.replace(cal_screen_string);
                        drop(locker);
                        break;
                    }
                    Some(_) => {
                        drop(locker);
                        sleep(Duration::from_millis(10));
                    }
                }
            }
        })
    }

    pub fn start_manager_thread(self: Arc<Self>) -> JoinHandle<()> {
        spawn(move || {
            loop {
                let mut locker = self.cal_screen_string.write().unwrap();
                match locker.take() {
                    None => {
                        drop(locker);
                        sleep(Duration::from_millis(10));
                    }
                    Some(cal) => {
                        let handle = cal_screen_worker_thread(cal, self.cache_out.clone());
                        handle.join().unwrap();
                        self.cache_ready_flag.store(true, Release);
                    }
                }
            }
        })
    }

    pub fn safe_get_cache_out(self: Arc<Self>) -> Option<Vec<String>> {
        if self.cache_ready_flag.load(Acquire) {
            let mut locker = self.cache_out.write().unwrap();
            let cache = locker.clone();
            locker.clear();
            self.cache_ready_flag.store(false, Release);
            return Some(cache);
        };
        None
    }
}

fn cal_screen_worker_thread(
    cal_screen_string: String,
    cache_out: Arc<RwLock<Vec<String>>>,
) -> JoinHandle<()> {
    spawn(move || {
        let cache = cal_screen_string
            .split("\n")
            .map(|s| s.to_owned())
            .collect::<Vec<_>>();
        let mut cache_locker = cache_out.write().unwrap();
        cache_locker.extend_from_slice(&cache);
        drop(cache_locker);
    })
}
